/**
 * Project Name:Micro-Services-Config-SpringFramework
 * File Name   :AnnotationScannerBean.java
 * Package Name:com.micro.services.config.springframework
 * Date:2015-9-7上午11:43:21
 * Copyright (c) 2015, http://my.oschina.net/httpssl All Rights Reserved.
 *
*/

package com.micro.services.config.springframework;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.micro.services.common.Constants;
import com.micro.services.common.utils.ReflectUtils;
import com.micro.services.config.annotation.Service;

public class AnnotationScannerBean  implements DisposableBean, BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware{

    private static final Logger logger = LoggerFactory.getLogger(Logger.class);
	
	private String annotationPackage;
	private String[] annotationPackages;
	
//    private final ConcurrentMap<String, ReferenceBean<?>> referenceConfigs = new ConcurrentHashMap<String, ReferenceBean<?>>();
	
	ApplicationContext applicationContext;
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	public String getAnnotationPackage() {
		return annotationPackage;
	}
	public void setAnnotationPackage(String annotationPackage) {
		logger.debug("annotationPackage is {}",annotationPackage);
		logger.debug("annotationPackages is {}",Arrays.toString(annotationPackages));
		this.annotationPackage = annotationPackage;
        this.annotationPackages = (annotationPackage == null || annotationPackage.length() == 0) ? null
                : annotationPackage.split(Constants.FILE_PATH_SEPARATOR);
	}
	
	private boolean isMatchPackage(Object bean) {
        if (annotationPackages == null || annotationPackages.length == 0) {
            return true;
        }
        String beanClassName = bean.getClass().getName();
        for (String pkg : annotationPackages) {
            if (beanClassName.startsWith(pkg)) {
                return true;
            }
        }
        return false;
    }
	
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		  if (!isMatchPackage(bean)) {
	            return bean;
	        }
	        Service service = bean.getClass().getAnnotation(Service.class);
	        if (service != null) {
//	            ServiceBean<Object> serviceConfig = new ServiceBean<Object>(service);
//	            if (void.class.equals(service.interfaceClass())
//	                    && "".equals(service.interfaceName())) {
//	                if (bean.getClass().getInterfaces().length > 0) {
//	                    serviceConfig.setInterface(bean.getClass().getInterfaces()[0]);
//	                } else {
//	                    throw new IllegalStateException("Failed to export remote service class " + bean.getClass().getName() + ", cause: The @Service undefined interfaceClass or interfaceName, and the service class unimplemented any interfaces.");
//	                }
//	            }
//	            if (applicationContext != null) {
//	                serviceConfig.setApplicationContext(applicationContext);
//	                if (service.registry() != null && service.registry().length > 0) {
//	                    List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
//	                    for (String registryId : service.registry()) {
//	                        if (registryId != null && registryId.length() > 0) {
//	                            registryConfigs.add((RegistryConfig)applicationContext.getBean(registryId, RegistryConfig.class));
//	                        }
//	                    }
//	                    serviceConfig.setRegistries(registryConfigs);
//	                }
//	                if (service.provider() != null && service.provider().length() > 0) {
//	                    serviceConfig.setProvider((ProviderConfig)applicationContext.getBean(service.provider(),ProviderConfig.class));
//	                }
//	                if (service.monitor() != null && service.monitor().length() > 0) {
//	                    serviceConfig.setMonitor((MonitorConfig)applicationContext.getBean(service.monitor(), MonitorConfig.class));
//	                }
//	                if (service.application() != null && service.application().length() > 0) {
//	                    serviceConfig.setApplication((ApplicationConfig)applicationContext.getBean(service.application(), ApplicationConfig.class));
//	                }
//	                if (service.module() != null && service.module().length() > 0) {
//	                    serviceConfig.setModule((ModuleConfig)applicationContext.getBean(service.module(), ModuleConfig.class));
//	                }
//	                if (service.provider() != null && service.provider().length() > 0) {
//	                    serviceConfig.setProvider((ProviderConfig)applicationContext.getBean(service.provider(), ProviderConfig.class));
//	                } else {
//	                    
//	                }
//	                if (service.protocol() != null && service.protocol().length > 0) {
//	                    List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
//	                    for (String protocolId : service.registry()) {
//	                        if (protocolId != null && protocolId.length() > 0) {
//	                            protocolConfigs.add((ProtocolConfig)applicationContext.getBean(protocolId, ProtocolConfig.class));
//	                        }
//	                    }
//	                    serviceConfig.setProtocols(protocolConfigs);
//	                }
//	                try {
//	                    serviceConfig.afterPropertiesSet();
//	                } catch (RuntimeException e) {
//	                    throw (RuntimeException) e;
//	                } catch (Exception e) {
//	                    throw new IllegalStateException(e.getMessage(), e);
//	                }
//	            }
//	            serviceConfig.setRef(bean);
//	            serviceConfigs.add(serviceConfig);
//	            serviceConfig.export();
	        }
		return bean;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return null;
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		if (annotationPackage == null || annotationPackage.length() == 0) {
            return;
        }
        if (beanFactory instanceof BeanDefinitionRegistry) {
            try {
                // init scanner
                Class<?> scannerClass = ReflectUtils.forName("org.springframework.context.annotation.ClassPathBeanDefinitionScanner");
                Object scanner = scannerClass.getConstructor(new Class<?>[] {BeanDefinitionRegistry.class, boolean.class}).newInstance(new Object[] {(BeanDefinitionRegistry) beanFactory, true});
                // add filter
//                Class<?> filterClass = ReflectUtils.forName("org.springframework.core.type.filter.AnnotationTypeFilter");
//                Object filter = filterClass.getConstructor(Class.class).newInstance(Service.class);
//                Method addIncludeFilter = scannerClass.getMethod("addIncludeFilter", ReflectUtils.forName("org.springframework.core.type.filter.TypeFilter"));
//                addIncludeFilter.invoke(scanner, filter);
                // scan packages
                String[] packages = annotationPackage.split(Constants.FILE_PATH_SEPARATOR);
                Method scan = scannerClass.getMethod("scan", new Class<?>[]{String[].class});
                scan.invoke(scanner, new Object[] {packages});
            } catch (Throwable e) {
            	//对于这种错误就要打印出来
               e.printStackTrace();
            }
        }
	}

	@Override
	public void destroy() throws Exception {
//		for (ServiceConfig<?> serviceConfig : serviceConfigs) {
//            try {
//                serviceConfig.unexport();
//            } catch (Throwable e) {
//                logger.error(e.getMessage(), e);
//            }
//        }
//        for (ReferenceConfig<?> referenceConfig : referenceConfigs.values()) {
//            try {
//                referenceConfig.destroy();
//            } catch (Throwable e) {
//                logger.error(e.getMessage(), e);
//            }
//        }
	}
}